﻿using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ComponentModel;

using SBPweb.Forge.Cryptography;
using SBPweb.Forge.Common.Exceptions;

namespace SBPweb.GoogleDataAPI.Client.Connector.Services.ClientService
{

    internal partial class ClientProvider :
        ClientProvider<ClientServiceClient, IClientService>,
        IClientProvider
    {

        #region Constructor logic

        public ClientProvider()
        {
            Client.AuthenticateCompleted += new EventHandler<AuthenticateCompletedEventArgs>(AuthenticateCompleted);
        }

        #endregion

        #region Methods

        protected override void Authenticating(string userName, string password)
        {
            Client.AuthenticateAsync(userName, password, this);
        }

        internal override void AuthenticateCompleted(object sender, AuthenticateCompletedEventArgs e)
        {
            base.AuthenticateCompleted(sender, e);
        }

        internal protected override string GetCryptographycSalt(string extra)
        {
            return string.Format("B5EC3466-3B8E-4aac-B6A5-67686F532F0B-{0}", extra);
        }

        #endregion

    }

    public abstract class ClientProvider<TClient, TContract> : ProviderBase<TClient, TContract>
        where TClient : class
        where TContract : class
    {

        #region Protected fields

        protected string authToken;

        #endregion

        #region Constructor logic

        public ClientProvider()
            : base()
        {
        }

        #endregion

        #region Property accessors

        #endregion

        #region Events

        public event EventHandler AuthenticationSucceed;

        public event EventHandler<ExceptionHandler.ExceptionEventArgs> AuthenticationFailed;

        #endregion

        #region Methods

        #region Operations

        public string Authenticate(string userName, string password)
        {
            string encryptedPass = crypter.Encrypt<string>(password, GetCryptographycSalt(userName));

            using (GetScope())
            {
                Authenticating(userName, encryptedPass);
            }

            return null;
        }

        #endregion

        #region Delegates / Event handlers

        protected abstract void Authenticating(string userName, string password);

        internal virtual void AuthenticateCompleted(object sender, AuthenticateCompletedEventArgs e)
        {
            try
            {
                if (e.UserState != this)
                {
                    return;
                }
                if (e.Error != null)
                {
                    throw e.Error;
                }

                authToken = e.Result;

                OnAuthenticationSucceed();
            }
            catch (Exception ex)
            {
                OnAuthenticationFailed(ex);
            }
        }

        protected void OnAuthenticationSucceed()
        {
            if (AuthenticationSucceed != null)
            {
                AuthenticationSucceed(this, EventArgs.Empty);
            }
        }

        protected void OnAuthenticationFailed(Exception ex)
        {
            if (AuthenticationFailed != null)
            {
                AuthenticationFailed(this, new ExceptionHandler.ExceptionEventArgs(ex, "Authentication"));
            }
        }

        #endregion

        #region Helper functions

        internal protected string GetCryptographycSalt()
        {
            return GetCryptographycSalt(string.Empty);
        }

        internal protected abstract string GetCryptographycSalt(string extra);

        internal protected override OperationContextScope GetScope()
        {
            OperationContextScope scope = base.GetScope();
            MaintainToken();
            return scope;
        }

        protected void MaintainToken()
        {
            if (!string.IsNullOrEmpty(authToken))
            {
                OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("AuthToken", "SBPwebTech", authToken));
            }
        }

        #endregion

        #endregion

    }
    
}